package org.carabiner.infinitest;

import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.swing.event.EventListenerList;

import junit.framework.Test;
import junit.framework.TestFailure;
import junit.framework.TestResult;
import junit.runner.BaseTestRunner;
import junit.runner.ReloadingTestSuiteLoader;

/**
 * 
 * @author <a href="mailto:benrady@gmail.com"Ben Rady</a>
 */
class JUnitRunner extends BaseTestRunner implements TestRunner
{
  private TestRunnerEventSupport eventSupport;
  
  private Set<String> testsRun;
  
  public JUnitRunner()
  {
    eventSupport = new TestRunnerEventSupport(this);
    testsRun = new HashSet<String>(); 
    setPreference("loading", "true");
    if (!(getLoader() instanceof ReloadingTestSuiteLoader))
      throw new IllegalStateException("Reloading class loaded not available!");        
  }
  
  protected void runFailed(String arg0)
  {
    System.out.println("Run Failed " + arg0);    
  }

  /**
   * msg is formatted as testNumber1(com.fakeco.fakeproduct.TestFakeProduct)
   */
  public void testEnded(String msg)
  {     
    testsRun.add(getMethodFromMessage(msg));    
    //System.out.println("Test ended " + msg);
  }

  public void testFailed(int arg0, Test test, Throwable arg2)
  {     
    //System.out.println("Test Failed " + arg0 + test.toString() + " Error: " + arg2.getMessage());
  }

  /**
   * msg is formatted as testNumber1(com.fakeco.fakeproduct.TestFakeProduct)
   */
  public void testStarted(String arg0)
  {
    //System.out.println("Test Started " + arg0);
  }
 
  public void runTests(Set<String> names)
  {
    for (Iterator<String> i = names.iterator(); i.hasNext();)
    {
      String testName = i.next();
      runTest(testName);
    }
  }   
  
  public void runTest(String testClass)
  {
    Test suite = getTest(testClass);

    TestResult result = new TestResult();
    testsRun.clear();
    result.addListener(this);    
    suite.run(result);    
    for(Enumeration<TestFailure> e = result.failures(); e.hasMoreElements();)
    {
      TestFailure failure = e.nextElement();
      String methodName = getMethodFromFailure(failure);
      eventSupport.fireTestFailed(testClass, failure.exceptionMessage(), failure.thrownException(), methodName);
      testsRun.remove(methodName);
    }    
    for(Enumeration<TestFailure> e = result.errors(); e.hasMoreElements();)
    {
      TestFailure error = e.nextElement();
      String methodName = getMethodFromFailure(error);
      eventSupport.fireTestError(getClassName(error), error.exceptionMessage(), error.thrownException(), methodName);
      testsRun.remove(methodName);
    }    
    for (String sucessfulTest : testsRun)
    {
      eventSupport.fireTestMethodSucceeded(testClass, sucessfulTest);
    }
    if (result.wasSuccessful())
      eventSupport.fireTestCaseSucceeded(testClass);    
  }

  private String getClassName(TestFailure failure)
  {
    return failure.failedTest().getClass().getName();
  }

  public void addTestStatusListener(TestStatusListener listener)
  {
    eventSupport.addTestStatusListener(listener);        
  }
        
  public static String getMethodFromFailure(TestFailure failure)
  {
    String message = failure.toString();
    return getMethodFromMessage(message);    
  }

  private static String getMethodFromMessage(String message)
  {
    return message.substring(0, message.indexOf('('));
  }

  public void killTest()
  {   
  }
}
